import { Tile as TileLayer, Vector as VectorLayer, Image as ImageLayer, Group as LayerGroup } from 'ol/layer'
import { XYZ, TileImage, TileArcGISRest, Vector as VectorSource, ImageWMS, ImageStatic, WMTS } from 'ol/source'

import WMTSTileGrid from 'ol/tilegrid/WMTS'
import TileGrid from 'ol/tilegrid/TileGrid'

import { Text, Fill, Stroke } from 'ol/style'
import { get as getProj } from 'ol/proj'
import { getWidth, getTopLeft } from 'ol/extent'

const layerHelper = {}
const tiandituKey = 'e8a7964b53b107e4e74dddf55c4c3d6e'

function stringDivider (str, width, spaceReplacer) {
  if (str.length > width) {
    let p = width
    for (; p > 0 && str[p] !== ' ' && str[p] !== '-'; p--) {}
    if (p > 0) {
      let left
      if (str.substring(p, p + 1) === '-') {
        left = str.substring(0, p + 1)
      } else {
        left = str.substring(0, p)
      }
      const right = str.substring(p + 1)
      return left + spaceReplacer + stringDivider(right, width, spaceReplacer)
    }
  }
  return str
}

function createXYZLayer (options) {
  const title = options.title
  const opacity = options.opacity || 1
  const maxResolution = options.maxResolution || undefined
  const minResolution = options.minResolution || undefined

  return new TileLayer({
    title: title,
    opacity: opacity,
    minResolution: minResolution,
    maxResolution: maxResolution,
    visible: options.visible || false,
    style: options.styleFunction,
    zIndex: options.zIndex || 0,
    source: new XYZ(options)
  })
}

function createGaodeXYZLayer (options) {
  const title = options.title
  const opacity = options.opacity || 1
  const maxResolution = options.maxResolution || undefined
  const minResolution = options.minResolution || undefined

  return new TileLayer({
    title: title,
    opacity: opacity,
    minResolution: minResolution,
    maxResolution: maxResolution,
    visible: options.visible || false,
    style: options.styleFunction,
    zIndex: options.zIndex || 0,
    source: new TileImage(options)
  })
}

function createARCGISTILELayer (options) {
  const title = options.title
  const opacity = options.opacity || 1
  const maxResolution = options.maxResolution || undefined
  const minResolution = options.minResolution || undefined

  return new TileLayer({
    title: title,
    opacity: opacity,
    minResolution: minResolution,
    maxResolution: maxResolution,
    visible: options.visible || false,
    zIndex: options.zIndex || 0,
    source: new TileArcGISRest({
      crossOrigin: 'anonymous',
      url: options.url,
      params: options.params
    })
  })
}

function createARCGISDynLayer (options) {
  const title = options.title
  const opacity = options.opacity || 1
  const zIndex = options.zIndex || 0
  const maxResolution = options.maxResolution || undefined
  const minResolution = options.minResolution || undefined

  return new TileLayer({
    title: title,
    opacity: opacity,
    zIndex: zIndex,
    visible: options.visible || false,
    minResolution: minResolution,
    maxResolution: maxResolution,
    source: new TileArcGISRest({
      crossOrigin: 'anonymous',
      url: options.url
    })
  })
}

function createBAIDUTILELayer (options) {
  const title = options.title
  const opacity = options.opacity || 1
  const maxResolution = options.maxResolution || undefined
  const minResolution = options.minResolution || undefined
  const projection = getProj('EPSG:3857')
  const resolutions = []
  for (let i = 0; i < 19; i++) {
    resolutions[i] = Math.pow(2, 18 - i)
  }
  const tilegrid = new TileGrid({
    origin: [0, 0],
    resolutions: resolutions
  })

  return new TileLayer({
    title: title,
    opacity: opacity,
    minResolution: minResolution,
    maxResolution: maxResolution,
    visible: options.visible || false,
    zIndex: options.zIndex || 0,
    source: new TileImage({
      projection: projection,
      tileGrid: tilegrid,
      tileUrlFunction: function (tileCoord, pixelRatio, proj) {
        if (!tileCoord) {
          return ''
        }
        const z = tileCoord[0]
        let x = tileCoord[1]
        let y = tileCoord[2]

        if (x < 0) {
          x = 'M' + -x
        }
        if (y < 0) {
          y = 'M' + -y
        }

        return options.url + '/?qt=tile&x=' + x + '&y=' + y + '&z=' + z + '&styles=pl&udt=20151021&scaler=1&p=1'
      }
    })
  })
}

// 通过WFS服务或地图查询服务返回矢量图层
function createVectorLayer (options) {
  const title = options.title
  const opacity = options.opacity || 1
  const source = options.sourceObject
  const maxResolution = options.maxResolution || undefined
  const minResolution = options.minResolution || undefined

  if (options.url !== '' && options.url != null && options.url !== undefined && typeof options.url !== 'function') {
    if (options.url.indexOf('?') !== -1) {
    } else {
      options.url =
        options.url +
        '?where=1%3D1&text=&objectIds=&time=&geometry=&geometryType=esriGeometryEnvelope&inSR=&spatialRel=esriSpatialRelIntersects&relationParam=&outFields=*&returnGeometry=true&maxAllowableOffset=&geometryPrecision=&outSR=3857&returnIdsOnly=false&returnCountOnly=false&orderByFields=&groupByFieldsForStatistics=&outStatistics=&returnZ=false&returnM=false&gdbVersion=&returnDistinctValues=false&f=pjson'
    }
  }

  return new VectorLayer({
    title: title,
    opacity: opacity,
    minResolution: minResolution,
    maxResolution: maxResolution,
    visible: options.visible || false,
    style: options.styleFunction,
    source: source || new VectorSource(options),
    zIndex: options.zIndex || 0
  })
}

// 创建WMS图层
function createWMSLayer (options) {
  // 例
  // new ol.layer.Image({
  //    source: new ol.source.ImageWMS({
  //        crossOrigin: 'anonymous',
  //        params: { 'LAYERS': '5,6,10' },
  //        serverType: 'mapserver',
  //        url: 'http://192.168.0.205:6081/proxy.ashx?http://192.168.0.193:6080/arcgis/services/hbmap/MapServer/WMSServer',
  //        projection: 'EPSG:3785'
  //    })
  // });

  const layer = new ImageLayer({
    source: new ImageWMS({
      crossOrigin: 'anonymous',
      params: options.params,
      serverType: 'mapserver',
      url: options.url
    }),
    title: options.title,
    visible: options.visible,
    zIndex: options.zIndex || 0
  })
  return layer
}

// 创建静态图片图层
function createImgLayer (options) {
  const layer = new ImageLayer({
    source: new ImageStatic({
      url: options.url,
      projection: options.projection,
      imageExtent: options.extent
    }),
    title: options.title,
    visible: options.visible,
    zIndex: options.zIndex || 0
  })
  return layer
}

// 在线天地图
function createTianDiTuLayer (options) {
  const projection = getProj('EPSG:4326')
  const projectionExtent = projection.getExtent()
  const size = getWidth(projectionExtent) / 256
  const resolutions = new Array(18)
  const matrixIds = new Array(18)
  for (let z = 1; z < 19; z++) {
    // generate resolutions and matrixIds arrays for this WMTS
    resolutions[z] = size / Math.pow(2, z)
    matrixIds[z] = z
  }
  // url: 'http://t0.tianditu.com/img_w/wmts', http://t0.tianditu.gov.cn/vec_c/wmts
  const types = options.url.split('/')[3].split('_')
  const layer = new TileLayer({
    title: options.title,
    visible: options.visible,
    zIndex: options.zIndex || 0,
    opacity: options.opacity || 1,
    source: new WMTS({
      crossOrigin: 'anonymous',
      // url: 'http://t0.tianditu.com/img_w/wmts', http://t0.tianditu.gov.cn/vec_c/wmts
      url: options.url + (options.url.indexOf('?') >= 0 ? '&tk=' : '?tk=') + tiandituKey,
      layer: options.layer || types[0], // 'img' || 'vec'|| ......
      matrixSet: options.matrixSet || types[1], // 'w' || 'c' || ......
      format: 'tiles',
      style: 'default',
      projection: projection,
      tileGrid: new WMTSTileGrid({
        origin: getTopLeft(projectionExtent),
        resolutions: resolutions,
        matrixIds: matrixIds
      })
    })
  })
  return layer
}

// 创建google图层
function createGoogleLayer (options) {
  const layer = new TileLayer({
    source: new TileImage({
      crossOrigin: 'anonymous',
      projection: getProj('EPSG:102100'),
      url: options.url // 'http://mt2.google.cn/vt/lyrs=y&hl=zh-CN&gl=CN&src=app&x={x}&y={y}&z={z}&s=G'
    }), // 加载谷歌影像地图
    title: options.title,
    visible: options.visible,
    zIndex: options.zIndex || 0,
    opacity: options.opacity || 1
  })
  return layer
}

layerHelper.createLayer = function (options) {
  let layer = null
  if (typeof options.style === 'object') {
    const style = options.style
    options.styleFunction = function (feature, resolution) {
      return style
    }
  }

  const source = options.source || ''
  if (source === 'XYZ') {
    layer = createXYZLayer(options)
  } else if (source === 'GaodeXYZ') {
    layer = createGaodeXYZLayer(options)
  } else if (source === 'arctile') {
    layer = createARCGISTILELayer(options)
  } else if (source === 'arcDyn') {
    layer = createARCGISDynLayer(options)
  } else if (source === 'baidutile') {
    layer = createBAIDUTILELayer(options)
  } else if (source === 'WMS') {
    layer = createWMSLayer(options)
  } else if (source === 'TDTTile') {
    layer = createTianDiTuLayer(options)
  } else if (source === 'google') {
    layer = createGoogleLayer(options)
  } else if (source === 'Image') {
    layer = createImgLayer(options)
  } else if (source === 'Vector') {
    const defaultTextStyle = {
      text: 'normal',
      align: 'left',
      baseline: 'middle',
      rotation: 0.0,
      font: 'Arial',
      weight: 'normal',
      size: '12px',
      offsetX: 10,
      offsetY: 0,
      color: '#000000',
      outline: '#ffffff',
      outlineWidth: 3,
      maxreso: 38400
    }
    const marking = options.marking || ''
    if (marking.length > 0) {
      const textStyle = options.textStyle || {}
      for (const key in defaultTextStyle) {
        if (!textStyle[key]) {
          textStyle[key] = defaultTextStyle[key]
        }
      }

      const getText = function (value, resolution, style) {
        const maxResolution = parseFloat(style.maxreso)
        let text = marking
        const type = style.text
        for (const key in value) {
          text = text.replace('[' + key + ']', value[key])
        }

        if (resolution > maxResolution) {
          text = ''
        } else if (type === 'hide') {
          text = ''
        } else if (type === 'shorten') {
          text = text.trunc(12)
        } else if (type === 'wrap') {
          text = stringDivider(text, 16, '\n')
        }
        if (text == null || text === undefined || text === '') {
          text = ''
        }
        return text
      }

      options.text = function (feature, resolution) {
        const value = feature.getProperties() || null
        if (value) {
          const text = getText(value, resolution, textStyle)
          return new Text({
            textAlign: textStyle.align,
            textBaseline: textStyle.baseline,
            font: textStyle.weight + ' ' + textStyle.size + ' ' + textStyle.font,
            text: text,
            fill: new Fill({
              color: textStyle.color
            }),
            stroke: new Stroke({
              color: textStyle.outline,
              width: parseInt(textStyle.outlineWidth, 10)
            }),
            offsetX: parseInt(textStyle.offsetX, 10),
            offsetY: parseInt(textStyle.offsetY, 10),
            rotation: parseFloat(textStyle.rotation)
          })
        }
      }

      if (typeof options.styleFunction === 'function') {
        const fn = options.styleFunction
        options.styleFunction = function (feature, resolution) {
          const style = fn(feature, resolution)
          try {
            style.setText(options.text(feature, resolution))
          } catch (e) {}

          return style
        }
      }
    }

    layer = createVectorLayer(options)
  }

  return layer
}

layerHelper.createGroup = function (options) {
  const layers = []
  const title = options.title
  const items = options.layers
  for (let len = items.length - 1; len >= 0; len--) {
    const layer = layerHelper.createLayer(items[len])
    if (layer) {
      layers.push(layer)
    }
  }

  if (layers.length === 0) {
    return null
  }
  return new LayerGroup({
    title: title,
    layers: layers
  })
}

export default layerHelper
